package services

import (
	"bufio"
	"context"
	"errors"
	"fmt"
	"github.com/zhongshaofa/swan-jobs/configs"
	"github.com/zhongshaofa/swan-jobs/internal/models"
	"github.com/zhongshaofa/swan-jobs/internal/plugin"
	"os"
	"os/exec"
	"strings"
	"sync"
	"time"
)

type LogRecord struct {
	Log       string
	Parameter ClientParameter
	Error     error
}

type ConsoleFileRecord struct {
	Path      string
	Parameter ClientParameter
}

var LogRecordChan = make(chan LogRecord, 100)
var ParameterChan = make(chan ClientParameter, 100)
var ConsoleFileRecordChan = make(chan ConsoleFileRecord, 100)

type ConsoleLogPath struct {
	parameter *ClientParameter
	suffix    int
}

type ConsoleLogPathManage struct {
	list          map[int]*ConsoleLogPath
	defaultSuffix int
	sync.Mutex
}

func (c *ConsoleLogPathManage) GetSuffix(parameter *ClientParameter) int {
	//c.Lock()
	//defer c.Unlock()
	if c.list != nil {
		_, exist := c.list[parameter.ScheduleId]
		if exist {
			return c.list[parameter.ScheduleId].suffix
		}
	}
	c.list[parameter.ScheduleId] = &ConsoleLogPath{
		parameter: parameter,
		suffix:    c.defaultSuffix,
	}
	return c.defaultSuffix
}

func (c *ConsoleLogPathManage) IncSuffix(parameter *ClientParameter) {
	//c.Lock()
	//defer c.Unlock()
	if c.list == nil {
		return
	}
	_, exist := c.list[parameter.ScheduleId]
	if exist {
		c.list[parameter.ScheduleId].suffix = c.list[parameter.ScheduleId].suffix + 1
	}
}

// 单例
var consoleLogPathManageInstance *ConsoleLogPathManage

func GetConsoleLogPathManageInstance() *ConsoleLogPathManage {
	once.Do(func() {
		consoleLogPathManageInstance = &ConsoleLogPathManage{
			list:          nil,
			defaultSuffix: 1,
		}
	})
	return consoleLogPathManageInstance
}

func Exec(parameter ClientParameter) {
	if parameter.Mode == models.ModeCron {
		execCron(parameter)
	} else {
		execSupervisor(parameter)
	}
}

func execCron(parameter ClientParameter) {
	ctx, cancel := context.WithTimeout(
		context.Background(),
		time.Duration(parameter.Timeout)*time.Second,
	)
	defer cancel()

	commandArgs := strings.Split(parameter.Command, " ")
	cmd := exec.CommandContext(ctx, commandArgs[0], commandArgs[1:]...)

	if len(parameter.Directory) > 0 {
		cmd.Dir = parameter.Directory
	}

	if parameter.Timeout > 0 && ctx.Err() == context.DeadlineExceeded {
		go chanLogRecord(LogRecord{
			Log:       "任务超时",
			Parameter: parameter,
			Error:     errors.New(fmt.Sprintf("DeadlineExceeded:进程运行时间超过%vs", parameter.Timeout)),
		})
		return
	}

	output, combinedErr := cmd.CombinedOutput()
	go chanLogRecord(LogRecord{
		Log:       string(output),
		Parameter: parameter,
		Error:     combinedErr,
	})
}

func execSupervisor(parameter ClientParameter) {
	commandArgs := strings.Split(parameter.Command, " ")
	cmd := exec.Command(commandArgs[0], commandArgs[1:]...)

	if len(parameter.Directory) > 0 {
		cmd.Dir = parameter.Directory
	}

	stdout, stdoutErr := cmd.StdoutPipe()
	cmd.Stderr = cmd.Stdout

	if stdoutErr != nil {
		go chanLogRecord(LogRecord{
			Log:       "stdoutErr",
			Parameter: parameter,
			Error:     errors.New(fmt.Sprintf("stdoutErr:%v", stdoutErr)),
		})
		return
	}

	startErr := cmd.Start()
	if startErr != nil {
		go chanLogRecord(LogRecord{
			Log:       "stdoutErr",
			Parameter: parameter,
			Error:     errors.New(fmt.Sprintf("stdoutErr:%v", startErr)),
		})
		return
	}

	// 更新任务队列
	GetClientSupervisorTaskManageInstance().Process(parameter.TaskId, cmd.Process)

	for {
		tmp := make([]byte, 1024)
		_, readErr := stdout.Read(tmp)
		go chanLogRecord(LogRecord{
			Log:       string(tmp),
			Parameter: parameter,
			Error:     nil,
		})
		if readErr != nil {
			break
		}
	}

	waitErr := cmd.Wait()
	if waitErr != nil {
		_ = GetClientSupervisorTaskManageInstance().Pause(parameter.TaskId)

		go chanLogRecord(LogRecord{
			Log:       "waitErr",
			Parameter: parameter,
			Error:     errors.New(fmt.Sprintf("waitErr:%v", waitErr)),
		})
		return
	}
	_ = GetClientSupervisorTaskManageInstance().Pause(parameter.TaskId)

	go chanLogRecord(LogRecord{
		Log:       "success",
		Parameter: parameter,
		Error:     nil,
	})
	return
}

func chanLogRecord(logRecord LogRecord) {
	LogRecordChan <- logRecord
}

// ClientChanListening 监听Chan内容处理
func ClientChanListening() {
	for {
		select {
		case logRecord := <-LogRecordChan:
			fmt.Printf("接收到的日志:%s,%+v\n", logRecord.Log, logRecord.Error)
			go saveConsoleLog(logRecord)
		case parameter := <-ParameterChan:
			if parameter.Mode == models.ModeSupervisor {
				go GetClientSupervisorTaskManageInstance().Add(&parameter)
			} else if parameter.Mode == models.ModeCron {
				go execCron(parameter)
			} else {
				plugin.Logger.Error(fmt.Sprintf("不支持此类型任务:%+v\n", parameter))
			}
		case consoleFileRecord := <-ConsoleFileRecordChan:
			fmt.Printf("新增console日志文件:%s\n", consoleFileRecord.Path)
		}
	}
}

// saveConsoleLog 保存控制台日志
func saveConsoleLog(logRecord LogRecord) {
	//suffix := GetConsoleLogPathManageInstance().GetSuffix(&logRecord.Parameter)
	suffix := 1
	filePath := fmt.Sprintf(
		"%s/SwanConsole-%v-%v-%v.log",
		configs.ClientConfig.ConsoleLogPath,
		logRecord.Parameter.TaskId,
		logRecord.Parameter.ScheduleId,
		suffix,
	)
	fileInfo, existErr := os.Stat(filePath)

	if existErr == nil {
		sizeMb := fileInfo.Size() / 1024 / 1034
		if sizeMb > 1 {
			//GetConsoleLogPathManageInstance().IncSuffix(&logRecord.Parameter)
			suffix = suffix + 1
			filePath = fmt.Sprintf(
				"%s/SwanConsole-%v-%v-%v.log",
				configs.ClientConfig.ConsoleLogPath,
				logRecord.Parameter.TaskId,
				logRecord.Parameter.ScheduleId,
				suffix,
			)
		}
	}

	file, err := os.OpenFile(filePath, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0666)
	if err != nil {
		plugin.Logger.Error(fmt.Sprintf("文件打开失败:%s", err))
		return
	}
	defer file.Close()
	if existErr != nil {
		ConsoleFileRecordChan <- ConsoleFileRecord{
			Path:      filePath,
			Parameter: logRecord.Parameter,
		}
	}
	write := bufio.NewWriter(file)
	logInfo := fmt.Sprintf("%s \r\n", logRecord.Log)
	write.WriteString(logInfo)
	write.Flush()
}
